﻿using System;
using System.Collections.Generic;
using MemoryPack;

namespace Hsenl {
    // UnbodiedHead 的存在就是为了找bodied, 而且是作为一种补充, 正常情况, 用户应该先给entity添加一个bodied, 然后再添加unbodied,
    // 而如果我们没添加的话, 那系统就会默认添加一个UnbodiedHead
    [MemoryPackable(GenerateType.CircularReference)]
    public partial class UnbodiedHeadOld : ScopeOld, IBodiedOld, IUnbodiedHeadOld {
        [MemoryPackIgnore]
        private BodiedOld _bodied;

        [MemoryPackIgnore]
        public BodiedOld Bodied {
            get => this._bodied;
            private set {
                if (this._bodied == value)
                    return;

                this._bodied = value;

                if (this.childrenScopes == null)
                    return;

                for (int i = 0, len = this.childrenScopes.Count; i < len; i++) {
                    var childScope = this.childrenScopes[i];
                    if (childScope is UnbodiedHeadOld unbodiedHead) {
                        unbodiedHead.Bodied = value;
                    }
                }
            }
        }

        [MemoryPackIgnore]
        public BodiedOld MainBodied => this.Bodied?.MainBodied;

        public override ScopeOld ParentScope {
            get => this.parentScope;
            internal set {
                if (this.parentScope == value)
                    return;

                this.OnBeforeParentScopeChangeInternal(value);

                // 父子关系确定
                var prevParent = this.parentScope;
                this.parentScope = value;

                if (prevParent != null) {
                    if (!prevParent.childrenScopes.Remove(this)) throw new Exception("???");
                    if (prevParent.childrenScopes.Count == 0)
                        prevParent.childrenScopes = null;
                }

                if (value != null) {
                    value.childrenScopes ??= new();
                    value.childrenScopes.Add(this);
                }

                if (prevParent != null) {
                    if (this.CombineMode == CombineMode.Auto) {
                        CrossDecombinMatchForParents(this, prevParent);
                    }

                    this.ForeachChildrenScope((childScope, _, p) => {
                        if (childScope.CombineMode == CombineMode.Auto) {
                            CrossDecombinMatchForParents(childScope, p); //
                        }
                    }, data: prevParent);
                }

                // 重写添加了这句 -> 确定父子关系后, head要处理的就是, 找到自己的bodied
                this.Bodied = this.FindScopeInParent<BodiedOld>();

                // 确立好父子关系后再进行跨域匹配, 保证形成组合的时候, 父子关系是正确的.
                if (value != null) {
                    if (this.CombineMode == CombineMode.Auto) {
                        this.CalcMaximumCrossLayerInTheory();
                        CrossCombinsMatchForParents(this, value, 1, null);
                    }

                    this.ForeachChildrenScope((childScope, layer, p) => {
                        if (childScope.CombineMode == CombineMode.Auto) {
                            childScope.CalcMaximumCrossLayerInTheory();
                            CrossCombinsMatchForParents(childScope, p, layer + 1, null); // 
                        }
                    }, data: value);
                }

                this.OnParentScopeChangedInternal(prevParent);
                prevParent?.OnChildScopeRemoveInternal(this);
                value?.OnChildScopeAddInternal(this);
            }
        }

        internal override void OnDeserializedInternal() {
            this.Entity.ForeachComponents((component, unbodiedHead) => {
                if (component is not UnbodiedOld unbodied)
                    return;

                unbodied.unbodiedHead = unbodiedHead;
            }, this);
        }

        internal override void OnDisposedInternal() {
            base.OnDisposedInternal();
            this._bodied = null;
        }

        internal override void OnComponentAddInternal(Component component) {
            if (component is ScopeOld)
                throw new Exception($"one entity only one scope. '{this.Name}' '{component.GetType().Name}'");

            if (component is not UnbodiedOld unbodied)
                return;

            unbodied.unbodiedHead = this;

            if (this.CombineMode != CombineMode.Manual) {
                CombinMatch(this, unbodied);
            }

            if (CombinationOld.CombinationCache.CrossCombinationInfoLookupTable.TryGetValue(unbodied.ComponentIndex, out var combinInfo)) {
                if (combinInfo.combinations1.Count != 0) {
                    if (this.CombineMode == CombineMode.Auto) {
                        if (this.ParentScope != null) {
                            if (this.HasComponentsAny(combinInfo.totalTypeCacher1)) {
                                foreach (var combination in combinInfo.combinations1) {
                                    CrossCombinMatchForParent(this, this.ParentScope, 1, combination);
                                }
                            }
                        }
                    }
                }
                
                if (combinInfo.combinations2.Count != 0) {
                    if (this.HasComponentsAny(combinInfo.totalTypeCacher2)) {
                        this.ForeachChildrenScope<(CrossCombinationInfo ci, ScopeOld p)>((child, layer, data) => {
                            if (child.CombineMode == CombineMode.Auto) {
                                foreach (var combination in data.ci.combinations2) {
                                    CrossCombinMatchForParent(child, data.p, layer, combination);
                                }
                            }
                        }, data: (combinInfo, this));
                    }
                }
            }
        }

        internal override void OnComponentRemoveInternal(Component component) {
            if (component is not UnbodiedOld unbodied)
                return;

            unbodied.unbodiedHead = null;

            if (this.CombineMode != CombineMode.Manual) {
                DecombinMatch(this, unbodied);
            }

            if (this.CombineMode == CombineMode.Auto) {
                CrossDecombinMatchByComponent(this, unbodied);
            }
        }
    }
}